﻿using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace InDepth2Demo.Chapter15
{
    /// <summary>
    /// 15.1 异步函数简介
    /// </summary>
    internal class AsynchrnousFunction
    {
        static readonly HttpClient client = new HttpClient();

        public static void Call()
        {
            Task<int> length = DisplayWebSiteLength();
            Console.WriteLine("1111");
            Console.WriteLine(length.IsCompleted);
            Thread.Sleep(5000);
            Console.WriteLine(length.IsCompleted);
        }

        public static async Task<int> DisplayWebSiteLength()
        {
            using (HttpClient client = new HttpClient())
            {
                string text = await client.GetStringAsync("https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/");
                return text.Length;
            }
        }

        public static async Task<int> DisplayWebSiteLength2()
        {
            using (HttpClient client = new HttpClient())
            {
                Task<string> task = client.GetStringAsync("https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/");
                string text = await task;           // 注意，task的类型是Task<string>，而await task表达式的类型是string。也就是说，await表达式执行的是“拆包”（unwrap）操作。
                return text.Length;
            }
        }
        // await 的主要目的是在等待耗时操作完成时避免阻塞。
        // 巧妙之处在于，方法在执行到 await 表达式时就返回了。



        //  调用方法为 PrintPageLength；
        //  异步方法为 GetPageLengthAsync；
        //  异步操作为 HttpClient.GetStringAsync；
        //  调用方法和异步方法之间的边界为 Task<int>；
        //  异步方法和异步操作之间的边界为 Task<string>。
        static async Task<int> GetPageLengthAsync(string url)
        {
            using (HttpClient client = new HttpClient())
            {
                Task<string> fetchTextTask = client.GetStringAsync(url);
                int length = (await fetchTextTask).Length;
                return length;
            }
        }
        static void PrintPageLength()
        {
            Task<int> lengthTask = GetPageLengthAsync("https://learn.microsoft.com/zh-cn/dotnet/visual-basic/programming-guide/concepts/async/?view=visualstudiosdk-2017");
            Console.WriteLine(lengthTask.Result);
        }


        // await表达式非常简单，只是在其他表达式前面加了一个await。
        // 一般来说，我们只能等待（await）一个异步操作。换句话说，是包含以下含义的操作：告知是否已经完成；如未完成可附加后续操作；获取结果，该结果可能为返回值，但至少可以指明成功或失败。



        // async 和 await 配套使用，返回值Task<T>
        public static async Task<int> TestTaskIntAsync()
        {
            Console.WriteLine("开始执行TestTaskAsync方法");
            int result = 0;
            Task task = new Task(() =>
            {
                Console.WriteLine("开始子线程耗时操作");
                Thread.Sleep(4000);
                Console.WriteLine("结束子线程耗时操作");
                result = 10;
            });
            task.Start();
            await task;
            Console.WriteLine("await关键字后面的内容 1");
            Console.WriteLine("await关键字后面的内容 2");
            return result;
        }
        public static void Call2Async()
        {
            Task<int> result = TestTaskIntAsync();
        }
        // 输出：
        //开始执行TestTaskAsync方法
        //开始子线程耗时操作
        //结束子线程耗时操作
        //await关键字后面的内容 1
        //await关键字后面的内容 2



        // 如果需要 I/O 绑定（例如从网络请求数据、访问数据库或读取和写入到文件系统），则需要利用异步编程。 还可以使用 CPU 绑定代码（例如执行成本高昂的计算），对编写异步代码而言，这是一个不错的方案。
        static async Task<int> GetPageLengthAsync2(string url)
        {
            Task<string> fetchTextTask = client.GetStringAsync(url);
            int length = (await fetchTextTask).Length;
            return length;
        }


        // await完成的task和未完成的task
        public static void Call2()
        {
            Task task = DemoCompletedAsync();
            Console.WriteLine("Method returned");
            task.Wait();
            Console.WriteLine("Task completed");
        }
        static async Task DemoCompletedAsync()
        {
            Console.WriteLine("Before first await");
            await Task.FromResult(10);              // Task.FromResult 总是返回一个已经完成的task
            Console.WriteLine("Between awaits");
            await Task.Delay(1000);
            Console.WriteLine("After second await");
        }
        //输出：
        //Before first await
        //Between awaits
        //Method returned
        // (这里等了1秒)
        //After second await
        //Task completed

        //关于执行顺序，有如下几个重点。
        //async方法在await已完成的task时不返回，此时方法还是按照同步方式执行。执行结果的前两行显示，这两行输出结果之间没有其他输出内容。
        //当async方法await延迟task时，async方法会立即返回。因此Main方法的第3行输出结果是Method returned。async方法能够得知当前await的操作尚未完成，因此选择返回以防止阻塞。
        //async方法返回的task只有在方法完成后才完成，因此Task completed在After second await之后打印。




        // async方法中失效的参数校验
        public static async Task MainAsync()
        {
            Task<int> task = ComputeLengthAsync(null);
            Console.WriteLine("Fetched the task");
            int length = await task;
            Console.WriteLine("Length: {0}", length);
        }

        static async Task<int> ComputeLengthAsync(string text)
        {
            if (text == null)
            {
                throw new ArgumentNullException("text");
            }
            await Task.Delay(500);
            return text.Length;
        }
    }

}
